package com.gbb.common.utils;

import android.util.Log;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import androidx.annotation.NonNull;

/**
 * 反射工具类
 *
 * @author zhaopuqing
 * @since v1.0
 */
public class ReflectionUtil {

	private static final String TAG = "ReflectionUtil";

	/**
	 * 根据class名字，调用默认的无参构造函数实例化对象
	 *
	 * @param className 类名
	 * @return 实例
	 */
	public static Object newInstance(@NonNull String className) {
		try {
			return Class.forName(className).newInstance();
		} catch(Exception e) {
			throw new RuntimeException("Cannot create " + className + ": " + e.getMessage());
		}
	}

	/**
	 * 实例化对象
	 *
	 * @param clazz
	 * @return
	 */
	public static <T> T newInstance(@NonNull Class<T> clazz) {
		try {
			return clazz.newInstance();
		} catch(Exception e) {
			throw new RuntimeException("Cannot create " + clazz.getName() + ": " + e.getMessage());
		}
	}


	/**
	 * 调用没有参数的方法,任何类型的访问域都可以调用
	 *
	 * @param method 方法
	 * @param target 方法所属的对象
	 * @return 方法调用的返回值
	 */
	public static Object invokeMethod(Method method,Object target) {
		return invokeMethod(method,target,(Object[])null);
	}

	/**
	 * 调用方法,任何类型的访问域都可以调用
	 *
	 * @param method 方法
	 * @param target 方法所属的对象
	 * @param args 参数可以是1至多个对象，或者对象数组
	 * @return 方法调用的返回值
	 */
	public static Object invokeMethod(Method method,Object target,Object... args) {
		makeAccessible(method);
		try {
			return method.invoke(target,args);
		} catch(Exception ex) {
			handleReflectionException(ex);
		}
		throw new IllegalStateException("Should never get here");
	}


	public static void makeAccessible(Field field) {
		if(!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
			field.setAccessible(true);
		}
	}

	/**
	 * Make the given method accessible, explicitly setting it accessible if
	 * necessary. The <code>setAccessible(true)</code> method is only called
	 * when actually necessary, to avoid unnecessary conflicts with a JVM
	 * SecurityManager (if active).
	 *
	 * @param method the method to make accessible
	 * @see Method#setAccessible
	 */
	public static void makeAccessible(Method method) {
		if(!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
			method.setAccessible(true);
		}
	}

	/**
	 * Make the given constructor accessible, explicitly setting it accessible
	 * if necessary. The <code>setAccessible(true)</code> method is only called
	 * when actually necessary, to avoid unnecessary conflicts with a JVM
	 * SecurityManager (if active).
	 *
	 * @param ctor the constructor to make accessible
	 * @see Constructor#setAccessible
	 */
	public static <T> void makeAccessible(Constructor<T> ctor) {
		if(!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) {
			ctor.setAccessible(true);
		}
	}

	/**
	 * Handle the given reflection exception. Should only be called if no
	 * checked exception is expected to be thrown by the target method.
	 * Throws the underlying RuntimeException or Error in case of an
	 * InvocationTargetException with such a root cause. Throws an
	 * IllegalStateException with an appropriate message else.
	 *
	 * @param ex the reflection exception to handle
	 */
	public static void handleReflectionException(Exception ex) {
		if(ex instanceof NoSuchMethodException) {
			throw new IllegalStateException("Method not found: " + ex.getMessage());
		}
		if(ex instanceof IllegalAccessException) {
			throw new IllegalStateException("Could not access method: " + ex.getMessage());
		}
		if(ex instanceof InvocationTargetException) {
			handleInvocationTargetException((InvocationTargetException)ex);
		}
		if(ex instanceof RuntimeException) {
			throw (RuntimeException)ex;
		}
		handleUnexpectedException(ex);
	}

	/**
	 * Handle the given invocation target exception. Should only be called if no
	 * checked exception is expected to be thrown by the target method.
	 * Throws the underlying RuntimeException or Error in case of such a root
	 * cause. Throws an IllegalStateException else.
	 *
	 * @param ex the invocation target exception to handle
	 */
	public static void handleInvocationTargetException(InvocationTargetException ex) {
		rethrowRuntimeException(ex.getTargetException());
	}

	/**
	 * Rethrow the given {@link Throwable exception}, which is presumably the
	 * <em>target exception</em> of an {@link InvocationTargetException}. Should
	 * only be called if no checked exception is expected to be thrown by the
	 * target method.
	 * Rethrows the underlying exception cast to an {@link RuntimeException} or
	 * {@link Error} if appropriate; otherwise, throws an
	 * {@link IllegalStateException}.
	 *
	 * @param ex the exception to rethrow
	 * @throws RuntimeException the rethrown exception
	 */
	public static void rethrowRuntimeException(Throwable ex) {
		if(ex instanceof RuntimeException) {
			throw (RuntimeException)ex;
		}
		if(ex instanceof Error) {
			throw (Error)ex;
		}
		handleUnexpectedException(ex);
	}

	/**
	 * Throws an IllegalStateException with the given exception as root cause.
	 *
	 * @param ex the unexpected exception
	 */
	private static void handleUnexpectedException(Throwable ex) {
		// Needs to avoid the chained constructor for JDK 1.4 compatibility.
		IllegalStateException isex = new IllegalStateException("Unexpected exception thrown",ex);
		throw isex;
	}

	/**
	 * 获取所有的属性，包括自己以及父类中 公共、保护、默认（包）访问和私有字段。
	 *
	 * @param clazz 类
	 * @return
	 */
	public static Field[] getDeclaredFields(final Class<?> clazz) {
		List<Field> fields = new ArrayList<>();
		for(Class<?> type = clazz; type != Object.class; type = type.getSuperclass()) {
			fields.addAll(Arrays.asList(type.getDeclaredFields()));
		}
		return fields.toArray(new Field[fields.size()]);
	}

	/**
	 * 通过反射,获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class. eg. public UserDao
	 * extends HibernateDao<User>
	 *
	 * @param clazz The class to introspect
	 * @return the first generic declaration, or Object.class if cannot be
	 * determined
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getSuperClassGenricType(final Class<?> clazz) {
		return getSuperClassGenricType(clazz,0);
	}

	/**
	 * 通过反射,获得定义Class时声明的父类的泛型参数的类型. 如无法找到, 返回Object.class.
	 * 如public UserDao extends HibernateDao<User,Long>
	 *
	 * @param clazz clazz The class to introspect
	 * @param index the Index of the generic ddeclaration,start from 0.
	 * @return the index generic declaration, or Object.class if cannot be
	 * determined
	 */
	@SuppressWarnings("rawtypes")
	public static Class getSuperClassGenricType(final Class clazz,final int index) {
		Type genType = null;
		Class<?> parentClass = clazz;
		do {
			genType = parentClass.getGenericSuperclass();
			if(genType instanceof ParameterizedType) {
				break;
			}
			parentClass = parentClass.getSuperclass();
			if(parentClass.equals(Object.class)) {
				Log.w(TAG,clazz.getSimpleName() + "'s superclass not ParameterizedType");
				return Object.class;
			}
		} while(true);

		Type[] params = ((ParameterizedType)genType).getActualTypeArguments();

		if(index >= params.length || index < 0) {
			Log.w(TAG,"Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + params.length);
			return Object.class;
		}
		if(!(params[index] instanceof Class)) {
			Log.w(TAG,clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
			return Object.class;
		}

		return (Class)params[index];
	}

	public static Class<?> getFiledGenricType(final Field field,final int index) {

		Type genType = field.getGenericType();

		if(!(genType instanceof ParameterizedType)) {
			Log.w(TAG,field.getName() + "'s field not ParameterizedType");
			return Object.class;
		}

		Type[] params = ((ParameterizedType)genType).getActualTypeArguments();

		if(index >= params.length || index < 0) {
			Log.w(TAG,"Index: " + index + ", Size of " + field.getName() + "'s Parameterized Type: " + params.length);
			return Object.class;
		}
		if(!(params[index] instanceof Class<?>)) {
			Log.w(TAG,field.getName() + " not set the actual class on superclass generic parameter");
			return Object.class;
		}

		return (Class<?>)params[index];
	}

	/**
	 * 从指定类中查找Annotation，可以选择是否从父类、父接口中查找
	 *
	 * @param <A>
	 * @param sourceClass
	 * @param annotationClass
	 * @param containSuperClass 是否从基类中查找
	 * @param containSuperInterface 是否从接口中查找
	 * @return
	 */
	public static <A extends Annotation> A getAnnotation(Class<?> sourceClass,Class<A> annotationClass,boolean containSuperClass,boolean containSuperInterface) {
		if(sourceClass == null) {
			return null;
		}
		A annotation = sourceClass.getAnnotation(annotationClass);
		if(annotation == null && containSuperClass) {
			annotation = getAnnotation(sourceClass.getSuperclass(),annotationClass,true,containSuperInterface);
		}
		if(annotation == null && containSuperInterface) {
			for(Class<?> clazz : sourceClass.getInterfaces()) {
				annotation = getAnnotation(clazz,annotationClass,containSuperClass,false);
				if(annotation != null) {
					return annotation;
				}
			}
		}
		return annotation;
	}

	/**
	 * 获取类的基类、接口中是superClazz子类或实现的所有Class集合
	 *
	 * @param clazz
	 * @param superClazz
	 * @return
	 */
	public static List<Class<?>> getSuperClassAndInterface(Class<?> clazz,Class<?> superClazz) {
		List<Class<?>> result = new ArrayList<>();
		Class<?> parentClass = clazz;
		do {
			if(superClazz.isAssignableFrom(parentClass)) {
				result.add(parentClass);
				parentClass = parentClass.getSuperclass();
			} else {
				break;
			}
			if(superClazz.isInterface()) {
				for(Class<?> interfaceClass : parentClass.getInterfaces()) {
					if(superClazz.isAssignableFrom(interfaceClass)) {
						result.add(interfaceClass);
					}
				}
			}

		} while(true);
		return result;
	}

}
